﻿using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Options;
using StackExchange.Redis;
using System;
using System.Collections.Generic;
using System.Security;
using System.Text;
using System.Threading.Tasks;
using Volo.Abp.DependencyInjection;

namespace Qianke.Core.Common.NameMutex
{

    public class RedisNameMutex : INameMutex, ITransientDependency
    {
        private readonly IOptions<NameMutexOptions> options;

        private IDatabase redisDb { set; get; } = null;

        private IConnectionMultiplexer _redisConnection { set; get; } = null;

        private string _keyName { set; get; } = null;
        private string _lockValue { set; get; } = Guid.NewGuid().ToString("N");

        public RedisNameMutex(IOptions<NameMutexOptions> options)
        {
            this.options = options;
        }

        public async Task EnterAsync()
        {
            var _expiryTime = TimeSpan.FromSeconds(options.Value.LockExpirySeconds);
            var outTime = DateTime.Now.AddSeconds(options.Value.WaitLockSeconds);
            while (DateTime.Now < outTime)
            {
                if (await redisDb.LockTakeAsync(_keyName, _lockValue, _expiryTime).ConfigureAwait(false))
                    return;
                await Task.Delay(100).ConfigureAwait(false);
            }
            throw new TimeoutException($"获取锁:{_keyName}超时");
        }

        public async Task ExitAsync()
        {
            if (redisDb != null)
            {
                await redisDb.LockReleaseAsync(_keyName, _lockValue);
                redisDb = null;
            }
        }

        public async Task InitializeAsync(string mutexName)
        {
            _keyName = mutexName;
            _redisConnection = await ConnectionMultiplexer.ConnectAsync(new ConfigurationOptions()
            {
                EndPoints = { { options.Value.RedisServerIP, options.Value.RedisServerPort } },
                Password = options.Value.RedisPassword
            });
            redisDb = _redisConnection.GetDatabase();
        }

        public async ValueTask DisposeAsync()
        {
            await ExitAsync();
            if (_redisConnection != null)
            {
                await _redisConnection.DisposeAsync();
                _redisConnection = null;
            }
        }

        public void Dispose()
        {
            _ = Task.Run(() => this.DisposeAsync());
        }
    }
}
